﻿using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Collections;
using System.Threading;
using FFACETools;
using System.ComponentModel;

namespace Server
{
    class socketServer
    {
        public AsyncCallback pfnWorkerCallBack;
        public Socket m_mainSocket;

        private FFACE _FFACE = null;
        private BackgroundWorker wker = null;

        public System.Collections.ArrayList m_workerSocketList =
        ArrayList.Synchronized(new System.Collections.ArrayList());

        //modifying this variable should be done in a thread safe manner
        public int m_clientCount = 0;

        public int m_Connections { get {return m_clientCount; } }

        public socketServer(int PID)
        {
            try
            {
                Setup SU = new Setup();
                SU.ShowDialog();

                _FFACE = new FFACE(PID);
                wker = new BackgroundWorker();
                wker.WorkerSupportsCancellation = true;
                wker.DoWork += new DoWorkEventHandler(wker_DoWork);

                

                int port = SU.Port;
                m_mainSocket = new Socket(AddressFamily.InterNetwork,
                    SocketType.Stream,
                    ProtocolType.Tcp);
                IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
                m_mainSocket.Bind(ipLocal);
                m_mainSocket.Listen(4);
                m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);

                wker.RunWorkerAsync();//this worker crap needs to be changed.
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }

        }

        void wker_DoWork(object sender, DoWorkEventArgs e)
        {
            //if logged in
            while (!wker.CancellationPending)
            {
                if (_FFACE.Player.LoggedIn <= 0)
                {
                    SendMsgToAll("Logged out of FFXI", System.Drawing.Color.Red);
                    break;
                }
                FFACE.ChatTools.ChatLine Line = _FFACE.Chat.GetNextLine(true, true);
                if (!string.IsNullOrEmpty(Line.Text))
                    SendMsgToAll(Line.Text, Line.Color);

                
                Thread.Sleep(200);
            }
        }

        public void OnClientConnect(IAsyncResult asyn)
        {
            try
            {
                Socket workerSocket = m_mainSocket.EndAccept(asyn);
                Interlocked.Increment(ref m_clientCount);
                m_workerSocketList.Add(workerSocket);

                // Send a welcome message to client
                string msg = "こんにちは\n";
                SendMsgToClient(msg, System.Drawing.Color.Red, m_clientCount);
                WaitForData(workerSocket, m_clientCount);

                m_mainSocket.BeginAccept(new AsyncCallback(OnClientConnect), null);
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\n OnClientConnection: Socket has been closed\n");
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }

        }

        public void WaitForData(System.Net.Sockets.Socket soc, int clientNumber)
        {
            try
            {
                if (pfnWorkerCallBack == null)
                {
                    pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
                }
                SocketPacket theSocPkt = new SocketPacket(soc, clientNumber);

                soc.BeginReceive(theSocPkt.dataBuffer, 0,
                    theSocPkt.dataBuffer.Length,
                    SocketFlags.None,
                    pfnWorkerCallBack,
                    theSocPkt);
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }
        }
        public void OnDataReceived(IAsyncResult asyn)
        {
            SocketPacket socketData = (SocketPacket)asyn.AsyncState;
            try
            {
                int iRx = socketData.m_currentSocket.EndReceive(asyn);
                for (int i = 0; i < iRx; i++)
                {
                    socketData.TransmissionBuffer.Add(socketData.dataBuffer[i]);
                }
                SocketPacket rcv = socketData.DeSerialize();

                _FFACE.Windower.SendString(System.Text.Encoding.GetEncoding(932).GetBytes(rcv.message));

                WaitForData(socketData.m_currentSocket, socketData.m_clientNumber);
            }
            catch (ObjectDisposedException)
            {
                System.Diagnostics.Debugger.Log(0, "1", "\nOnDataReceived: Socket has been closed\n");
            }
            catch (SocketException se)
            {
                if (se.ErrorCode == 10054) // Error code for Connection reset by peer
                {
                    m_workerSocketList[socketData.m_clientNumber - 1] = null;
                }
                else
                {
                    MessageBox.Show(se.Message);
                }
            }
        }

        void SendMsgToClient(string msg, System.Drawing.Color color, int clientNumber)
        {
            SocketPacket snd = new SocketPacket((Socket)m_workerSocketList[clientNumber - 1], clientNumber);
            snd.message = msg;
            snd.color = color;
            Socket workerSocket = (Socket)snd.m_currentSocket;
            workerSocket.Send(snd.Serialize());
        }
        void SendMsgToAll(string msg, System.Drawing.Color color)
        {
            try
            {
                byte[] byData = System.Text.Encoding.GetEncoding(932).GetBytes(msg);
                Socket workerSocket = null;
                for (int i = 0; i < m_workerSocketList.Count; i++)
                {
                    workerSocket = (Socket)m_workerSocketList[i];
                    if (workerSocket != null)
                    {
                        if (workerSocket.Connected)
                        {
                            SocketPacket snd = new SocketPacket(workerSocket, i);
                            snd.message = msg;
                            snd.color = color;
                            workerSocket.Send(snd.Serialize());
                        }
                    }
                }
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message);
            }
        }
    }
}
